#include <linux/mm.h>
#include <linux/kernel.h>

#include <asm/system.h>

#define NR_LAST_FREE_PAGES 32
static unsigned long last_free_pages[NR_LAST_FREE_PAGES] = {
    0,
};

static inline void add_mem_queue(unsigned long addr, unsigned long *queue) {
    addr &= PAGE_MASK;
    *(unsigned long *)addr = *queue;
    *queue = addr;
}

void free_page(unsigned long addr) {
    if (addr < high_memory) {
        unsigned short *map = mem_map + MAP_NR(addr);

        if (*map) {
            if (!(*map & MAP_PAGE_RESERVED)) {
                unsigned long flag;

                save_flags(flag);
                cli();
                if (!--*map) {
                    add_mem_queue(addr, &free_page_list);
                    nr_free_pages++;
                }
                restore_flags(flag);
            }
            return;
        }
        printk("Trying to free free memory (%08lx): memory probabably corrupted\n", addr);
        printk("PC = %08lx\n", *(((unsigned long *)&addr) - 1));
        return;
    }
}

unsigned long __get_free_page(int priority) {
    unsigned long result, flag;
    static unsigned long index = 0;

    save_flags(flag);
    cli();
    if ((result = free_page_list) != 0) {
        if (!(result & ~PAGE_MASK) && result < high_memory) {
            free_page_list = *(unsigned long *)result;
            if (!mem_map[MAP_NR(result)]) {
                mem_map[MAP_NR(result)] = 1;
                nr_free_pages--;
                last_free_pages[index = (index + 1) & (NR_LAST_FREE_PAGES - 1)] = result;
                restore_flags(flag);
                return result;
            }
            printk("Free page %08lx has mem_map = %d\n",
                   result, mem_map[MAP_NR(result)]);
        } else
            printk("Result = 0x%08lx - memory map destroyed\n", result);
        free_page_list = 0;
        nr_free_pages = 0;
    } else if (nr_free_pages) {
        printk("nr_free_pages is %d, but free_page_list is empty\n", nr_free_pages);
        nr_free_pages = 0;
    }
    restore_flags(flag);
    return 0;
}
